package com.ae.shield.common.items.shieldFittings.makers;

import com.ae.shield.AEShieldMod;
import com.ae.shield.api.IShield;
import com.ae.shield.common.enchantment.shieldSuffix.SuffixFactory;
import com.ae.shield.common.items.util.DamageSorter;
import com.ae.shield.common.items.util.ItemNBTHelper;
import com.ae.shield.common.items.util.KeyHelper;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.energy.IEnergyStorage;
import org.lwjgl.glfw.GLFW;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.text.DecimalFormat;
import java.util.List;

/**
 * 护盾生成器的基础类
 * 调用final属性来获得经过后缀修饰的属性值
 * get属性获得初始值
 */
public class ShieldMakerBase extends Item implements IShield {
    protected final float maxIntensity;
    protected final float upperLimit;
    protected final float floorLimit;
    protected final float recovery;
    protected final float close;
    protected final float remote;
    protected final float magic;
    protected final float other;
    protected final float penalty;
    protected final float cd;
    protected final int maxPiles;
    protected final int cost;

    public ShieldMakerBase(Properties properties) {
        this(properties, new ShieldProperty());
    }

    public ShieldMakerBase(Properties properties, ShieldProperty shieldPropertyIn) {
        super(properties.group(AEShieldMod.AE_SHIELD).maxStackSize(1));
        maxIntensity = shieldPropertyIn.maxIntensity;
        upperLimit = shieldPropertyIn.upperLimit;
        floorLimit = shieldPropertyIn.floorLimit;
        recovery = shieldPropertyIn.recovery;
        close = shieldPropertyIn.close;
        remote = shieldPropertyIn.remote;
        magic = shieldPropertyIn.magic;
        other = shieldPropertyIn.other;
        penalty = shieldPropertyIn.penalty;
        cd = shieldPropertyIn.cd;
        maxPiles = shieldPropertyIn.maxPiles;
        cost = shieldPropertyIn.cost;
    }

    @Override
    public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantment) {
        return false;
    }

    @Override
    public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
        return false;
    }

    @Override
    public boolean hasEffect(@Nonnull ItemStack itemStack) {
        return false;
    }

//----------------------------------------------------------------------------------------------------------------------


    @Override
    public void onCreated(@Nonnull ItemStack stack, @Nonnull World worldIn, @Nonnull PlayerEntity playerIn) {
        setIntensity(stack, 0f);
        setPlies(stack, 0);
        setCD(stack, 0);
        reloadProperty(stack);
    }

    @Override
    public void addInformation(@Nonnull ItemStack stackIn, @Nullable World worldIn, @Nonnull List<ITextComponent> components, @Nonnull ITooltipFlag flagIn) {
        if (KeyHelper.isKeyPressed(GLFW.GLFW_KEY_LEFT_SHIFT)) {
            ITextComponent upperLimit = new StringTextComponent(" " + new DecimalFormat("#0.00").format(finalUpperLimit(stackIn))).mergeStyle(TextFormatting.BLUE);
            components.add(new TranslationTextComponent("tooltip.ae.shield.upper_limit", upperLimit).mergeStyle(TextFormatting.GRAY));

            ITextComponent floorLimit = new StringTextComponent(" " + new DecimalFormat("#0.00").format(finalFloorLimit(stackIn))).mergeStyle(TextFormatting.BLUE);
            components.add(new TranslationTextComponent("tooltip.ae.shield.floor_limit", floorLimit).mergeStyle(TextFormatting.GRAY));

            ITextComponent recoveryRate = new StringTextComponent(" " + new DecimalFormat("#0.00").format(finalRecovery(stackIn))).mergeStyle(TextFormatting.BLUE);
            ITextComponent cost = new StringTextComponent(Integer.toString(finalCost(stackIn))).mergeStyle(TextFormatting.BLUE);
            components.add(new TranslationTextComponent("tooltip.ae.shield.recovery_rate", recoveryRate, cost).mergeStyle(TextFormatting.GRAY));

            components.add(new TranslationTextComponent("tooltip.ae.shield.rate").mergeStyle(TextFormatting.GRAY));

            ITextComponent close = new StringTextComponent(new DecimalFormat("#0.00").format(finalClose(stackIn))).mergeStyle(TextFormatting.RED);
            ITextComponent remote = new StringTextComponent(new DecimalFormat("#0.00").format(finalRemote(stackIn))).mergeStyle(TextFormatting.GREEN);
            components.add(new TranslationTextComponent("tooltip.ae.shield.rate_1", close, remote).mergeStyle(TextFormatting.WHITE));

            ITextComponent magic = new StringTextComponent(new DecimalFormat("#0.00").format(finalMagic(stackIn))).mergeStyle(TextFormatting.DARK_PURPLE);
            ITextComponent other = new StringTextComponent(new DecimalFormat("#0.00").format(finalOther(stackIn))).mergeStyle(TextFormatting.DARK_AQUA);
            components.add(new TranslationTextComponent("tooltip.ae.shield.rate_2", magic, other).mergeStyle(TextFormatting.WHITE));

            components.add(new StringTextComponent(""));

            addShiftInformation(components, stackIn);
        } else {
            ITextComponent shift = new StringTextComponent("SHIFT").mergeStyle(TextFormatting.BLUE);
            components.add(new TranslationTextComponent("tooltip.ae.shield.press", shift).mergeStyle(TextFormatting.GRAY));
        }

        if (KeyHelper.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)) {
            addCtrlInformation(components, stackIn);
        } else {
            ITextComponent shift = new StringTextComponent("CTRL").mergeStyle(TextFormatting.BLUE);
            components.add(new TranslationTextComponent("tooltip.ae.shield.press", shift).mergeStyle(TextFormatting.GRAY));
        }

        components.add(new StringTextComponent(""));

        ITextComponent intensity = new StringTextComponent(new DecimalFormat("#0.00").format(getIntensity(stackIn))).mergeStyle(TextFormatting.BLUE);
        ITextComponent maxIntensity = new StringTextComponent(new DecimalFormat("#0.00").format(finalMaxIntensity(stackIn))).mergeStyle(TextFormatting.WHITE);
        components.add(new TranslationTextComponent("tooltip.ae.shield.intensity1").mergeStyle(TextFormatting.GRAY));
        components.add(new TranslationTextComponent("tooltip.ae.shield.intensity2", intensity, maxIntensity).mergeStyle(TextFormatting.GRAY));

        ITextComponent plies = new StringTextComponent(Integer.toString(getPlies(stackIn))).mergeStyle(TextFormatting.BLUE);
        ITextComponent maxPlies = new StringTextComponent(Integer.toString(finalMaxPiles(stackIn))).mergeStyle(TextFormatting.WHITE);
        components.add(new TranslationTextComponent("tooltip.ae.shield.plies1").mergeStyle(TextFormatting.GRAY));
        components.add(new TranslationTextComponent("tooltip.ae.shield.plies2", plies, maxPlies).mergeStyle(TextFormatting.GRAY));

        ITextComponent cd = new StringTextComponent(new DecimalFormat("#0.00").format(getCD(stackIn))).mergeStyle(TextFormatting.BLUE);
        ITextComponent maxCD = new StringTextComponent(new DecimalFormat("#0.00").format(finalMaxCD(stackIn))).mergeStyle(TextFormatting.WHITE);
        components.add(new TranslationTextComponent("tooltip.ae.shield.cd1").mergeStyle(TextFormatting.GRAY));
        components.add(new TranslationTextComponent("tooltip.ae.shield.cd2", cd, maxCD).mergeStyle(TextFormatting.GRAY));

    }

    /**
     * shift添加的文本信息在此处写入
     */
    public void addShiftInformation(List<ITextComponent> components, ItemStack stack) {
    }


    /**
     * ctrl添加的文本信息在此处写入
     */
    public void addCtrlInformation(List<ITextComponent> components, ItemStack stack) {
    }

    /**
     * 用于处理护盾如何抵消伤害，并依据伤害来削减护盾强度和层数
     *
     * @return 未能完全抵消的伤害
     */
    @Override
    public float onEntityHurt(LivingEntity livingEntity, ItemStack stack, float amount, DamageSource source, IEnergyStorage energyCap) {
        if (getPlies(stack) < 1) {
            return amount;
        }

        if (consumeCalculator(stack, amount, source) == 0f) {
            return amount;
        }

        float residue = onShieldStartWork(livingEntity, stack, amount, source, energyCap);

        if (residue == 0) {
            return 0f;
        }

        //如果低于了触发值时的行为
        if (residue < finalFloorLimit(stack)) {
            residue = onBelowLimit(stack, residue);
            if (residue == -1f) {
                return amount;
            }
        }

        //如果超过了上阈值时的行为
        if (residue > finalUpperLimit(stack)) {
            residue = onExceedLimit(stack, residue);
        }

        residue = consumeCalculator(stack, residue, source);

        residue = residue == 0f ? 0f : amount * consumeShield(stack, residue, 0) / residue;

        //如果对护盾造成了伤害则刷新cd
        if (residue < amount) {
            setCD(stack, finalMaxCD(stack));
            onShieldWorked(livingEntity, stack, amount, residue, source, energyCap);
        }

        return residue;
    }

    /**
     * 当护盾在计算伤害是否对护盾有效后，阈值判定之前触发
     *
     * @return 需要护盾进行计算的伤害
     */
    public float onShieldStartWork(LivingEntity livingEntity, ItemStack stack, float amount, DamageSource source, IEnergyStorage energyCap) {
        return amount;
    }

    /**
     * 当护盾进行了一次工作时触发
     *
     * @return 之后需要其他护盾进行计算的伤害
     */
    public float onShieldWorked(LivingEntity livingEntity, ItemStack stack, float amount, float residue, DamageSource source, IEnergyStorage energyCap) {
        return residue;
    }

    /**
     * 用于处理护盾的恢复
     * 每调用一次，恢复一次
     */
    public void rebuildShield(ItemStack stack, IEnergyStorage energyCap) {
        int cost = finalCost(stack);
        if (getPlies(stack) >= finalMaxPiles(stack) && getIntensity(stack) >= finalMaxIntensity(stack)) {
            return;
        }

        if (energyCap.extractEnergy(cost, true) != cost) {
            return;
        }

        energyCap.extractEnergy(cost, false);
        recoverShield(stack, finalRecovery(stack), 0);
    }

    /**
     * 消耗指定强度和层数，通常只需要输入需要消耗的强度即需要抵抗的伤害即可
     *
     * @param intensity 要消耗的护盾强度
     * @param plies     额外需要要消耗的护盾层数
     * @return 返回未能完全消耗的伤害
     */
    public float consumeShield(ItemStack stack, float intensity, int plies) {
        float total = plies * finalMaxIntensity(stack) + intensity;

        if (total > getIntensity(stack)) {
            if (getPlies(stack) > 1) {
                total = total - getIntensity(stack);
                setPlies(stack, getPlies(stack) - 1);
                setIntensity(stack, finalMaxIntensity(stack));
                return consumeShield(stack, total, 0);
            } else {
                setIntensity(stack, 0f);
                setPlies(stack, 0);
                return total - getIntensity(stack);
            }
        } else {
            setIntensity(stack, getIntensity(stack) - total);
            return 0f;
        }
    }

    /**
     * 回复指定强度与层数的护盾
     *
     * @param intensity 要恢复的强度
     * @param plies     要恢复的层数
     */
    public void recoverShield(ItemStack stack, float intensity, int plies) {
        if (plies + getPlies(stack) > finalMaxPiles(stack)) {
            setIntensity(stack, finalMaxIntensity(stack));
            setPlies(stack, finalMaxPiles(stack));
            return;
        } else {
            setPlies(stack, getPlies(stack) + plies);
        }

        float i1 = getIntensity(stack) + intensity;
        if (i1 > finalMaxIntensity(stack)) {
            if (getPlies(stack) + 1 <= finalMaxPiles(stack)) {
                i1 = i1 - finalMaxIntensity(stack);

                if (getPlies(stack) == 0) {
                    setPlies(stack, 2);
                    setIntensity(stack, 0);
                } else {
                    setIntensity(stack, 0);
                    setPlies(stack, getPlies(stack) + 1);
                    recoverShield(stack, i1, 0);
                }

            } else {
                setIntensity(stack, finalMaxIntensity(stack));
            }
        } else {
            setIntensity(stack, i1);
            if (getPlies(stack) == 0) {
                setPlies(stack, 1);
            }
        }
    }

    /**
     * 当受到的伤害超出阈值时的行为
     * 在伤害抵消的初始调用
     * 默认操作，扣除当前所有强度并额外损失一层
     *
     * @param amount 承受的伤害值
     * @return 返回需要继续被护盾抵抗的伤害值
     */
    public float onExceedLimit(ItemStack stack, float amount) {
        consumeShield(stack, finalPenalty(stack) + amount, 0);
        return 0;
    }

    /**
     * 当受到的伤害低于触发值时的行为
     * 返回-1f跳过该护盾的触发
     *
     * @param amount 承受的伤害
     * @return 返回需要继续被护盾抵抗的伤害值
     */
    public float onBelowLimit(ItemStack stack, float amount) {
        return -1f;
    }

    /**
     * 依据护盾生成器的属性以及后缀对护盾消耗量进行计算
     *
     * @param amount 原本要消耗的强度
     * @param source 伤害类型
     * @return 实际消耗的强度
     */
    public float consumeCalculator(ItemStack stack, float amount, DamageSource source) {
        if (amount <= 0) {
            return 0;
        }

        float consume = amount;

        switch (DamageSorter.sort(source)) {
            case CLOSE:
                consume = finalClose(stack) * amount;
                break;
            case REMOTE:
                consume = finalRemote(stack) * amount;
                break;
            case MAGIC:
                consume = finalMagic(stack) * amount;
                break;
            case OTHER:
                consume = finalOther(stack) * amount;
        }

        return consume;
    }

    @Override
    public boolean onEntityDeath(ItemStack stack, LivingEntity livingEntity, DamageSource source, IEnergyStorage energyStorage) {
        return false;
    }

    /**
     * 由护盾容器触发，只有当护盾被放在容器中时触发
     */
    public void shieldTick(@Nonnull ItemStack stack, @Nonnull World world, @Nonnull Entity entity, IEnergyStorage energyCap) {
    }

//---------------------------------------------------音效----------------------------------------------------------------

    //TODO 确定音效
    public void playDefenseVoice(World world, LivingEntity entity) {
        world.playSound(null, entity.getPosX(), entity.getPosY(), entity.getPosZ(), SoundEvents.BLOCK_BELL_USE, SoundCategory.PLAYERS, 5f, 0.5f);
    }

    public void playBreakVoice(World world, LivingEntity entity) {
        world.playSound(null, entity.getPosX(), entity.getPosY(), entity.getPosZ(), SoundEvents.BLOCK_CHAIN_BREAK, SoundCategory.PLAYERS, 5f, 0.5f);
    }

//----------------------------------------------------护盾值操作----------------------------------------------------------

    private final String TAG_INTENSITY = "intensity";
    private final String TAG_PLIES = "plies";

    public float getIntensity(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_INTENSITY, 0f);
    }

    public void setIntensity(ItemStack stack, float intensity) {
        ItemNBTHelper.setFloat(stack, TAG_INTENSITY, intensity);
    }

    public int getPlies(ItemStack stack) {
        return ItemNBTHelper.getInt(stack, TAG_PLIES, 0);
    }

    public void setPlies(ItemStack stack, int plies) {
        ItemNBTHelper.setInt(stack, TAG_PLIES, plies);
    }

    public float getCD(ItemStack stack) {
        return ItemNBTHelper.getInt(stack, TAG_CD_COUNT, 0);
    }

    public void setCD(ItemStack stack, float cd) {
        ItemNBTHelper.setFloat(stack, TAG_CD_COUNT, cd);
    }

//-------------------------------------------------getter---------------------------------------------------------------

    public float getMaxIntensity() {
        return maxIntensity;
    }

    public float getUpperLimit() {
        return upperLimit;
    }

    public float getFloorLimit() {
        return floorLimit;
    }

    public float getRecovery() {
        return recovery;
    }

    public float getClose() {
        return close;
    }

    public float getRemote() {
        return remote;
    }

    public float getMagic() {
        return magic;
    }

    public float getOther() {
        return other;
    }

    public float getPenalty() {
        return penalty;
    }

    public float getMaxCD() {
        return cd;
    }

    public int getMaxPiles() {
        return maxPiles;
    }

    public int getCost() {
        return cost;
    }

    //---------------------------------------------获取后缀修饰后的属性---------------------------------------------------------
    //这些tag用于存储经过后缀修饰后的属性
    private final String TAG_MAX_INTENSITY = "max_intensity";
    private final String TAG_UPPER_LIMIT = "upper_limit";
    private final String TAG_FLOOR_LIMIT = "floor_limit";
    private final String TAG_RECOVERY = "recovery";
    private final String TAG_CLOSE = "close";
    private final String TAG_REMOTE = "remote";
    private final String TAG_MAGIC = "magic";
    private final String TAG_OTHER = "other";
    private final String TAG_PENALTY = "penalty";
    private final String TAG_MAX_PILES = "max_piles";
    private final String TAG_RECOVER_COST = "recover_cost";
    private final String TAG_CD = "cd";
    private final String TAG_CD_COUNT = "cd_count";

    //
    public void reloadProperty(ItemStack stack) {
        ItemNBTHelper.setFloat(stack, TAG_MAX_INTENSITY, SuffixFactory.maxIntensity(stack));
        ItemNBTHelper.setFloat(stack, TAG_UPPER_LIMIT, SuffixFactory.upperLimit(stack));
        ItemNBTHelper.setFloat(stack, TAG_FLOOR_LIMIT, SuffixFactory.floorLimit(stack));
        ItemNBTHelper.setFloat(stack, TAG_RECOVERY, SuffixFactory.recovery(stack));
        ItemNBTHelper.setFloat(stack, TAG_CLOSE, SuffixFactory.close(stack));
        ItemNBTHelper.setFloat(stack, TAG_REMOTE, SuffixFactory.remote(stack));
        ItemNBTHelper.setFloat(stack, TAG_MAGIC, SuffixFactory.magic(stack));
        ItemNBTHelper.setFloat(stack, TAG_OTHER, SuffixFactory.other(stack));
        ItemNBTHelper.setFloat(stack, TAG_PENALTY, SuffixFactory.penalty(stack));
        ItemNBTHelper.setFloat(stack, TAG_CD, SuffixFactory.cd(stack));
        ItemNBTHelper.setInt(stack, TAG_MAX_PILES, SuffixFactory.maxPiles(stack));
        ItemNBTHelper.setInt(stack, TAG_RECOVER_COST, SuffixFactory.recoverCost(stack));

        if (getPlies(stack) > finalMaxPiles(stack)) {
            setPlies(stack, finalMaxPiles(stack));
            setIntensity(stack, finalMaxIntensity(stack));
        }

        if (getIntensity(stack) > finalMaxIntensity(stack)) {
            setIntensity(stack, finalMaxIntensity(stack));
        }
    }

    public float finalMaxIntensity(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_MAX_INTENSITY, maxIntensity);
    }

    public float finalUpperLimit(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_UPPER_LIMIT, upperLimit);
    }

    public float finalFloorLimit(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_FLOOR_LIMIT, floorLimit);
    }

    public float finalRecovery(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_RECOVERY, recovery);
    }

    public float finalClose(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_CLOSE, close);
    }

    public float finalRemote(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_REMOTE, remote);
    }

    public float finalMagic(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_MAGIC, magic);
    }

    public float finalOther(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_OTHER, other);
    }

    public float finalPenalty(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_PENALTY, penalty);
    }

    public float finalMaxCD(ItemStack stack) {
        return ItemNBTHelper.getFloat(stack, TAG_CD, cd);
    }

    public int finalMaxPiles(ItemStack stack) {
        return ItemNBTHelper.getInt(stack, TAG_MAX_PILES, maxPiles);
    }

    public int finalCost(ItemStack stack) {
        return ItemNBTHelper.getInt(stack, TAG_RECOVER_COST, cost);
    }

//-----------------------------------------------护盾属性内部类-----------------------------------------------------------

    public static class ShieldProperty {
        private float maxIntensity = 10f;
        private float upperLimit = 20f;
        private float floorLimit = 0f;
        private float recovery = 1f;
        private float close = 1f;
        private float remote = 1f;
        private float magic = 1f;
        private float other = 1f;
        private float penalty = 10f;
        private float cd = 1f;
        private int maxPiles = 1;
        private int cost = 1;

        /**
         * 最高强度
         */
        public ShieldMakerBase.ShieldProperty maxIntensity(float maxIntensity) {
            this.maxIntensity = maxIntensity;
            return this;
        }

        /**
         * 承受伤害上阈值
         */
        public ShieldMakerBase.ShieldProperty upperLimit(float upperLimit) {
            this.upperLimit = upperLimit;
            return this;
        }

        /**
         * 护盾触发阈值
         */
        public ShieldMakerBase.ShieldProperty floorLimit(float floorLimit) {
            this.floorLimit = floorLimit;
            return this;
        }

        /**
         * 恢复速率，为 recoverRate/tick
         */
        public ShieldMakerBase.ShieldProperty recovery(float recoveryRate) {
            this.recovery = recoveryRate;
            return this;
        }

        /**
         * 近战消耗倍率
         */
        public ShieldMakerBase.ShieldProperty close(float closeRate) {
            this.close = closeRate;
            return this;
        }

        /**
         * 远程消耗倍率
         */
        public ShieldMakerBase.ShieldProperty remote(float remoteRate) {
            this.remote = remoteRate;
            return this;
        }

        /**
         * 魔法消耗倍率
         */
        public ShieldMakerBase.ShieldProperty magic(float magicRate) {
            this.magic = magicRate;
            return this;
        }

        /**
         * 其他消耗倍率
         */
        public ShieldMakerBase.ShieldProperty other(float otherRate) {
            this.other = otherRate;
            return this;
        }

        /**
         * 超出上限的惩罚
         */
        public ShieldMakerBase.ShieldProperty penalty(float penalty) {
            this.penalty = penalty;
            return this;
        }

        /**
         * 从停止攻击到开始恢复护盾的间隔
         * 单位为tick
         */
        public ShieldMakerBase.ShieldProperty cd(float cd) {
            this.cd = cd;
            return this;
        }

        /**
         * 最高层数
         */
        public ShieldMakerBase.ShieldProperty maxPiles(int maxPiles) {
            this.maxPiles = maxPiles;
            return this;
        }

        /**
         * 每次恢复消耗的能量
         */
        public ShieldMakerBase.ShieldProperty cost(int recoverCost) {
            this.cost = recoverCost;
            return this;
        }

    }
}
